package org.bot.client.flow;

import com.alibaba.fastjson.JSONObject;
import lombok.Getter;
import org.bot.api.config.ClientConfig;
import org.bot.api.event.DataStorage;
import org.bot.api.event.EventName;
import org.bot.api.flow.FlowJudgeType;
import org.bot.api.flow.FlowNode;
import org.bot.api.flow.NodeServiceType;
import org.bot.api.util.StringUtil;
import org.bot.api.source.SourceManage;
import org.bot.client.driver.api.server.ServerReceiveClientData;
import org.bot.client.storage.MapDataStorage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;

/**
 * @author 沧月
 * @create 0015 2023/9/15 09:59
 * @description <流程执行器>
 */

@Component
public class FlowExecute {

    static Logger logger = LoggerFactory.getLogger(FlowExecute.class);

    @Autowired
    ServerReceiveClientData serverClientInfo;

    @Autowired
    FlowConstructorV2 flowConstructorV2;

    @Autowired
    SourceManage sourceManage;

    @Getter
    private final FixedSizeContainer taskContainer = new FixedSizeContainer(ClientConfig.getBotConfig().getMaxQueue());

    public boolean execute(JSONObject flowData, String clientId) {
        String flowId = flowData.getString(FlowDataAttrFiled.FLOW_ID.getKey());
        FlowTask flowTask = new FlowTask();

        FlowClientExecuteLog log = new FlowClientExecuteLog();
        log.putStartTime(StringUtil.getTime())
                .putFlowId(flowId)
                .putBatchId(flowTask.getBatchId())
                .putCode(ClientErrorCode.CODE_1.getCode());
        try {
            FlowNode flowNode = flowConstructorV2.buildFlowNode(flowData.getJSONObject(FlowDataAttrFiled.BOT_JSON.getKey()), flowId);
            flowTask.setFlowData(flowNode);
            flowTask.setFlowId(flowId);
            flowTask.setDebug(flowData.getBooleanValue(FlowDataAttrFiled.DEBUG.getKey()));
            flowTask.setClientId(clientId);
            flowTask.setFlowExecute(this);
            flowTask.setFlowName(flowData.getString(FlowDataAttrFiled.NAME.getKey()));
        } catch (Exception e) {
            logger.error("运行异常:", e);
            log.putCode(ClientErrorCode.CODE__2.getCode());
            serverClientInfo.clientLog(log);
            return false;
        }

        try {
            serverClientInfo.clientLog(log);
            taskContainer.add(flowTask);
        } catch (Exception e) {
            logger.error("运行异常:", e);
            log.putCode(ClientErrorCode.CODE__3.getCode());
            serverClientInfo.clientLog(log);
            return false;
        }
        return true;
    }


    public void runTask(FlowTask take) {
        FlowClientExecuteLog log = new FlowClientExecuteLog();
        DataStorage dataList = new MapDataStorage();
        try {
            //更新客户端状态
            this.taskContainer.updateClientStatus(take, 0);
            //收集日志
            log.putFlowId(take.getFlowId());
            log.putBatchId(take.getBatchId());
            log.putCode(ClientErrorCode.CODE_2.getCode());
            serverClientInfo.clientLog(log);

            logger.debug("批次【{}】 开始执行任务【{}】...", take.getBatchId(), take.getFlowName());
            boolean status = this._execute(take.getFlowData(), dataList, take);
            logger.debug("批次【{}】 任务【{}】执行结束,开始释放占有资源...", take.getBatchId(), take.getFlowName());

            log.fluentPut("endTime", StringUtil.getTime());
            if (status) {
                log.putCode(ClientErrorCode.CODE_0.getCode())
                        .putMsg("执行成功!");
            } else {
                log.putCode(ClientErrorCode.CODE__1.getCode())
                        .putMsg("执行失败!");
            }
        } catch (Exception e) {
            log.putCode(ClientErrorCode.CODE__1.getCode())
                    .putMsg("执行失败!");
            logger.error("运行异常:", e);
        } finally {
            //释放资源
            sourceManage.releaseAllSource(dataList);
            logger.debug("批次【{}】 释放占有资源完成...", take.getBatchId());

            //推送日志
            taskContainer.removeTask(take);
            logger.debug("批次【{}】 任务【{}】结束，推送日志到服务端...", take.getBatchId(), take.getFlowName());
            serverClientInfo.clientLog(log);
        }
    }

    private boolean _execute(FlowNode flowNode, DataStorage dataList, FlowTask task) {
        FlowExecuteLog log;
        while (true) {
            log = new FlowExecuteLog();
            try {
                log.putArg(flowNode.getArg());
                log.putFlowName(flowNode.getName());
                log.putEvent(flowNode.getEvent());
                log.putBatchId(task.getBatchId());
                log.putOrder(task.getOrder());

                task.taskInterrupt();//判断任务是否需要终止

                final long startTime = System.currentTimeMillis();
                Object data = flowNode.getEvent().exec(flowNode, dataList);
                logger.debug("批次【{}】 [{}] 执行节点【{}】,监听器【{}】 耗时: {} ms,生产数据内容：【{}】", task.getBatchId(), log.getOrder(),
                        flowNode.getName(), flowNode.getEvent().getClass()
                                .getAnnotation(EventName.class).value(),
                        System.currentTimeMillis() - startTime, data == null ? "无" : data);

                log.putEndTime();
                dataList.put(flowNode.getNodeId(), data);

                log.putEventData(data);
                log.putStatus(ExecuteStatus.SUCCESS);
                serverClientInfo.clientExecLog(log);

                if (!flowNode.getNextNote().isEmpty()) {
                    String flowJudgeType;
                    switch (flowNode.getFlowType()) {
                        case JUDGE:
                            if (data == null) {
                                //如果是null默认为NO
                                flowJudgeType = FlowJudgeType.NO.name();
                                break;
                            } else if (data instanceof Boolean) {
                                //如果是Boolean类型
                                if (!(boolean) data) {
                                    flowJudgeType = FlowJudgeType.NO.name();
                                } else {
                                    flowJudgeType = FlowJudgeType.YES.name();
                                }
                                break;
                            } else {
                                //其它自定义类型
                                flowJudgeType = data.toString();
                                break;
                            }
                        case SERVICE:
                        default:
                            flowJudgeType = FlowJudgeType.YES.name();
                    }
                    FlowNode nextFlowNode = flowNode.getNextNote().get(flowJudgeType);
                    if (nextFlowNode != null) {
                        nextFlowNode.setDriveWeb(dataList.get(nextFlowNode.getWebWindowName()));
                        flowNode = nextFlowNode;
                    } else {
                        break;
                    }
                } else {
                    break;
                }
            } catch (Exception e) {
                logger.error("运行异常:", e);
                log.putErrorMsg(e.getMessage());
                log.putEndTime();
                log.putStatus(ExecuteStatus.ERROR);
                serverClientInfo.clientExecLog(log);
                return false;
            }
        }
        return true;
    }
}
